package com.code.test.second.dp;

/**
 * https://segmentfault.com/a/1190000043478051
 */
public class BagProblem01 {

    public static void main(String[] args) {
        int[] weight = {4, 3, 1};
        int[] value = {15, 20, 30};
        int bagSize = 4;
        maxBagValue(weight, value, bagSize);
    }

    /**
     * 动态规划获得结果
     *
     * @param weight  物品的重量
     * @param value   物品的价值
     * @param bagSize 背包的容量
     */
    public static void testWeightBagProblem(int[] weight, int[] value, int bagSize) {

        // 创建dp数组
        int goods = weight.length;  // 获取物品的数量
        int[][] dp = new int[goods][bagSize + 1];

        // 初始化dp数组
        // 创建数组后，其中默认的值就是0
        for (int j = weight[0]; j <= bagSize; j++) {
            dp[0][j] = value[0];
        }

        // 填充dp数组
        for (int i = 1; i < weight.length; i++) {
            //先遍历物品，再遍历背包
            for (int j = 1; j <= bagSize; j++) {
                //如果背包容量<小于当前物品容量,即已经放不下了，则不放
                if (j < weight[i]) {
                    /**
                     * 当前背包的容量都没有当前物品i大的时候，是不放物品i的
                     * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
                     */
                    dp[i][j] = dp[i - 1][j];
                } else {
                    /**
                     * 当前背包的容量可以放下物品i
                     * 那么此时分两种情况：
                     *    1、不放物品i
                     *    2、放物品i
                     * 比较这两种情况下，哪种背包中物品的最大价值最大
                     */
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }

        // 打印dp数组
        for (int i = 0; i < goods; i++) {
            for (int j = 0; j <= bagSize; j++) {
                System.out.print(dp[i][j] + "\t");
            }
            System.out.println("\n");
        }
    }

    public static void maxBagValue(int[] weight, int[] value, int bagSize) {

        /**
         * 1、dp[i][j]代表在0..i物品里取任意个，装进容量为j的背包里，价值最大化
         * 所以每个dp[i][j]都是当下选择里面的价值最大化
         * 2、dp[i][j] = 选择放或不放物品i
         * 不放: dp[i-1][j] ，物品i重量再放进去就超重了，所以以上一个为准
         * 放: dp[i-1][j-weight[i]]+value[i],不带物品i的价值(要扣掉物品i的重量)+物品i的价值
         * 放：找出不带物品i时价值最大化是多少
         *
         *
         */

        int rowLen = weight.length;

        int[][] dp = new int[rowLen][bagSize + 1];

        //初始化
        for (int col = weight[0]; col <= bagSize; col++) {
            dp[0][col] = value[0];
        }

        //先遍历物品
        for (int row = 1; row < weight.length; row++) {
            for (int col = 1; col <= bagSize; col++) {
                //如果当前物品重量比背包大，即放不下
                if (col < weight[row]) {
                    //即不放，用上一个
                    dp[row][col] = dp[row - 1][col];
                } else {
                    dp[row][col] = Math.max(dp[row - 1][col], dp[row - 1][col - weight[row]] + value[row]);
                }
            }
        }

        // 打印dp数组
        for (int i = 0; i < rowLen; i++) {
            for (int j = 0; j <= bagSize; j++) {
                System.out.print(dp[i][j] + "\t");
            }
            System.out.println("\n");
        }
    }
}
